package com.chixing.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chixing.entity.Product;
import com.chixing.mapper.ProductMapper;
import com.chixing.service.IProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chixing.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author smith
 * @since 2025-09-28
 */
@Service
public class ProductServiceImpl implements IProductService {
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private RedisTemplate redisTemplate; //用于操作Redis缓存

    //初始化2个lua脚本
    private DefaultRedisScript<Long> decrCountScript; //减库存脚本
    private DefaultRedisScript<Long> delLockScript; //删除锁脚本

    //初始化的时候将这两个脚本加载进来
    public ProductServiceImpl(){
        //加载lua脚本
        decrCountScript =  new DefaultRedisScript<Long>();
        decrCountScript.setResultType(Long.class);
        decrCountScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/redis_decr_kucun.lua")));

        delLockScript =  new DefaultRedisScript<Long>();
        delLockScript.setResultType(Long.class);
        delLockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/redis_del_lock.lua")));
    }

    @Override
    public Result getById(Integer proId) {
        String key = "product_" + proId;
        if (redisTemplate.hasKey(key)){//redis中存在这个key，直接返回该商品
            System.out.println("redis中已存在该商品");
            Product product = (Product) redisTemplate.opsForValue().get(key);
            return Result.getSuccess(product);
        }else {
            System.out.println("redis中没有该商品，执行sql语句查询");
            Product product = productMapper.selectById(proId);
            if (product!=null){
                redisTemplate.opsForValue().set(key,product);
                return Result.getSuccess(product);
            }else
                return Result.getFail(proId);
        }
    }

    /**
     * 1.先到Redis缓存中查询，Redis缓存中没有，则从数据库查询一份，放到Redis缓存中
     * 2. Redis缓存中有数据，则直接返回
     */
    @Override
    public Result getHotProduct() {
        String hotProductsKey = "hotProducts";
        List<Product> productList = null;
        if (!redisTemplate.hasKey(hotProductsKey)){
            Page<Product> page = new Page<>(1, 5);
            QueryWrapper<Product> wrapper = new QueryWrapper<>();
            wrapper.orderByDesc("pro_like_count");
            wrapper.eq("pro_status" , 1);
            wrapper.select("pro_id","pro_name","pro_img","pro_price","pro_count");
            page = productMapper.selectPage(page,wrapper);
            productList = page.getRecords();
            productList.forEach(System.out::println);
            //把热销商品列表放到数据库中
            Long rows = redisTemplate.opsForList().leftPushAll(hotProductsKey, productList);
            //把单个商品放到redis中
            for (Product product:productList) {
                String key = "pro_count_" + product.getProId();
                Integer value = product.getProCount();
                redisTemplate.opsForValue().set(key,value);
                product.setProCount(null);//热销商品的页面是不需要库存数量的
            }
        }else {
            productList = redisTemplate.opsForList().range(hotProductsKey,0,-1);
        }
        return Result.getSuccess(productList);
    }


    //购买商品 减库存
    @Override
    public Result decrProCount(Integer proId) {
        //1.为了防止高并发，减库存 出现脏数据 ，redis锁机制
        String lockKey = "lock_pro_" + proId;
        //为了防止释放别人的锁加UUID
        String lockValue = UUID.randomUUID().toString().replace("-","");
        String proCountKey = "pro_count_" + proId;
        //锁上面加上过期时间 防止出现死锁
        boolean isGetLock = redisTemplate.opsForValue().setIfAbsent(lockKey,lockValue,10,TimeUnit.MINUTES);
        if (isGetLock){
            try {
                //2.获得该商品的库存判断是否足够，减库存 lua
                Integer count = (Integer) redisTemplate.opsForValue().get(proCountKey);
                System.out.println("目前的库存是:" + count);
                if (count!=null && count>0){
                    redisTemplate.execute(decrCountScript, Arrays.asList(proCountKey),1);
                    System.out.println("当前线程" + Thread.currentThread().getName() + "减库存成功----------");
                }else {
                    System.out.println("库存已不足，当前线程" + Thread.currentThread().getName());
                }
            }catch (Exception e){
                System.out.println("减库存执行业务有异常------");
                e.printStackTrace();
            }finally {
                //3.释放锁 （要判断释放的锁是不是自己加的锁）
                Long rows = (Long) redisTemplate.execute(this.delLockScript, Arrays.asList(lockKey), lockValue);
                if (rows>0){
                    System.out.println("当前线程" +Thread.currentThread().getName()+"释放锁成功！");
                    return Result.getSuccess(proId);
                }else {
                    System.out.println("当前线程" + Thread.currentThread().getName()+"释放锁失败!");
                    return Result.getFail(proId);
                }
            }
        }else {
            //该商品被其它用户加锁，继续尝试获得
            decrProCount(proId);
        }
        return Result.getFail(proId);
    }


}
